package com.yj.idempotence.thread;

/**
 * 生产者与消费者三种实现方式
 * 在实现生产者消费者问题时，可以采用三种方式：
 *
 * 使用 Object 的 wait/notify 的消息通知机制，本节课程我们采用该方式结合 synchronized 关键字进行生产者与消费者模式的实现；
 * 使用 Lock 的 Condition 的 await/signal 的消息通知机制；
 * 使用 BlockingQueue 实现。本文主要将这三种实现方式进行总结归纳。
 */


import java.util.LinkedList;

/**
 * 场景设计：
 *
 * 创建一个工厂类 ProductFactory，该类包含两个方法，produce 生产方法和 consume 消费方法；
 * 对于 produce 方法，当没有库存或者库存达到 10 时，停止生产。为了更便于观察结果，每生产一个产品，sleep 5000 毫秒；
 * 对于 consume 方法，只要有库存就进行消费。为了更便于观察结果，每消费一个产品，sleep 5000 毫秒；
 * 库存使用 LinkedList 进行实现，此时 LinkedList 即共享数据内存；
 * 创建一个 Producer 生产者类，用于调用 ProductFactory 的 produce 方法。生产过程中，要对每个产品从 0 开始进行编号；
 * 创建一个 Consumer 消费者类，用于调用 ProductFactory 的 consume 方法；
 * 创建一个测试类，main 函数中创建 2 个生产者和 3 个消费者，运行程序进行结果观察。
 */

public class Demo05{
    public static void main(String[] args) {
        ProductFactory productFactory = new ProductFactory();
        new Thread(new Producer(productFactory),"生产者").start();
        new Thread(new Consumer(productFactory),"消费者").start();
    }
}
class ProductFactory {
    private LinkedList<String> products; //根据需求定义库存，用 LinkedList 实现
    private int capacity = 10; // 根据需求：定义最大库存 10
    public ProductFactory() {
        products = new LinkedList<String>();
    }
    // 根据需求：produce 方法创建
    public synchronized void produce(String product) {
        while (capacity == products.size()) { //根据需求：如果达到 10 库存，停止生产
            try {
                System.out.println("警告：线程("+Thread.currentThread().getName() + ")准备生产产品，但产品池已满");
                wait(); // 库存达到 10 ，生产线程进入 wait 状态
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        products.add(product); //如果没有到 10 库存，进行产品添加
        try {
            Thread.sleep(5000); //根据需求为了便于观察结果，每生产一个产品，sleep 5000 ms
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程("+Thread.currentThread().getName() + ")生产了一件产品:" + product+";当前剩余商品"+products.size()+"个");
        notify(); //生产了产品，通知消费者线程从 wait 状态唤醒，进行消费
    }

    // 根据需求：consume 方法创建
    public synchronized String consume() {
        while (products.size()==0) { //根据需求：没有库存消费者进入wait状态
            try {
                System.out.println("警告：线程("+Thread.currentThread().getName() + ")准备消费产品，但当前没有产品");
                wait(); //库存为 0 ，无法消费，进入 wait ，等待生产者线程唤醒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        String product = products.remove(0) ; //如果有库存则消费，并移除消费掉的产品
        try {
            Thread.sleep(5000);//根据需求为了便于观察结果，每消费一个产品，sleep 5000 ms
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程("+Thread.currentThread().getName() + ")消费了一件产品:" + product+";当前剩余商品"+products.size()+"个");
        notify();// 通知生产者继续生产
        return product;
    }
}
class Producer implements Runnable {
    private ProductFactory productFactory; //关联工厂类，调用 produce 方法
    public Producer(ProductFactory productFactory) {
        this.productFactory = productFactory;
    }
    public void run() {
        int i = 0 ; // 根据需求，对产品进行编号
        while (true) {
            productFactory.produce(String.valueOf(i)); //根据需求 ，调用 productFactory 的 produce 方法
            i++;
        }
    }
}
class Consumer implements Runnable {
    private ProductFactory productFactory;
    public Consumer(ProductFactory productFactory) {
        this.productFactory = productFactory;
    }
    public void run() {
        while (true) {
            productFactory.consume();
        }
    }
}